【精选】MyBatis+MySQL8.0存取Json字段之TypeHandler

您所在的位置:网站首页 gson json转数组 【精选】MyBatis+MySQL8.0存取Json字段之TypeHandler

【精选】MyBatis+MySQL8.0存取Json字段之TypeHandler

2023-11-19 08:26| 来源: 网络整理| 查看: 265

MyBatis+MySQL8.0存取Json字段之TypeHandler 一丶背景二丶解决方案1.自定义转换2.继承BaseTypeHandler实现对JSON类型的转换三丶反思,继续扩张认知边界

一丶背景

在业务开发过程中,为了实现一个在线编辑器功能,存取了一些CSS样式在MySQL里面,就像这样:

"css": { "id": "3", "width": 11, "height": 12, "left": 13, "top": 14, "createtTime": "2021-10-14 10:40:11", "updateTime": "2021-10-14 10:40:11", "creatorId": "1111" },

之前的MySQL存JSON字段都是用BLOB类型,在MySQL 5.7后添加了JSON类型作为对JSON字段的存储。但是MyBatis并未支持,因此有了这篇文章要解决的问题。

二丶解决方案 1.自定义转换

也就是查询的时候,将用fastjson这类工具包,JSON串转为对象;在存储时,手动将对象转为json串,然后存入类型为VARCHAR的字段里。就像这样:

@Data public class Element { private String id; private String css; private Objetc cssObj; private Datetime createTime; } @Data public class Css{ private String id; private int width; private int height; private int left; private int top; private Date createTime; private Date updateTime; private String creatorId; }

自定义转换ServiceImpl:

@Override public Element getById(String id) { Element element = elementDao.selectByPrimaryKey(id); String css= element .getCss(); Css cccObj = JsonUtils.fromJson(css, Css.class); element .setCssObj(cccObj ); return element ; } @Override public boolean save(Element element) { Css cssObj = element.getCssObj(); element.setCss(JsonUtil.toJson(cssObj )); return elementDao.insert(element) > 0; }

这样的解决方案存在两个问题: 1.需要在model类上面增加冗余字段:cssObj。 2.每一个业务逻辑里面都需要增加上述转换方法,代码冗余了些。 3.不够优雅。 这时候,阅读框架源码的作用体现出来了,Mybatis本身作为一个ORM框架,自己是实现了类型转换的,可不可以参考Mybatis的实现,来自己设计一个转换器呢?Mybatis预定义的基础类型转换是通过实现TypeHandler接口或者继承抽象类BaseTypeHandler来实现,其默认的转换类型如图:在这里插入图片描述 本文采用的方式是继承BaseTypeHandler的方式,来实现对JSON数据类型的转换。Mybatis的BaseTypeHandler具体代码如图: BaseTypeHandler:

public abstract class BaseTypeHandler extends TypeReference implements TypeHandler { protected Configuration configuration; public void setConfiguration(Configuration c) { this.configuration = c; } public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { if (parameter == null) { if (jdbcType == null) { throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters."); } try { ps.setNull(i, jdbcType.TYPE_CODE); } catch (SQLException e) { throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " + "Cause: " + e, e); } } else { setNonNullParameter(ps, i, parameter, jdbcType); } } public T getResult(ResultSet rs, String columnName) throws SQLException { T result = getNullableResult(rs, columnName); if (rs.wasNull()) { return null; } else { return result; } } public T getResult(ResultSet rs, int columnIndex) throws SQLException { T result = getNullableResult(rs, columnIndex); if (rs.wasNull()) { return null; } else { return result; } } public T getResult(CallableStatement cs, int columnIndex) throws SQLException { T result = getNullableResult(cs, columnIndex); if (cs.wasNull()) { return null; } else { return result; } } public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException; public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException; public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException; } 2.继承BaseTypeHandler实现对JSON类型的转换 第一步,定义一个abstract class,继承于org.apache.ibatis.type.BaseTypeHandler,作为Object类型的转换基类,所有想varchar与Object的互转,只需要继承此基类即可,无需重复写第一个方法那些自定义转换的步骤。 package com.eqxiu.chart.handler; import com.eqxiu.chart.util.JsonUtils; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * ClassName: AbsractObjectTypeHandler * Description: Model对象json转换抽象类 解决mybatis插入json数据报错问题 * Author: lizhiyu * Date: 2021/10/14 11:05 * Version: mvp **/ public abstract class AbstractObjectTypeHandler extends BaseTypeHandler { @Override public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, JsonUtils.toJson(parameter)); } @Override public T getNullableResult(ResultSet rs, String columnName) throws SQLException { String data = rs.getString(columnName); return StringUtils.isBlank(data) ? null : JsonUtils.fromJson(data, (Class) getRawType()); } @Override public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String data = rs.getString(columnIndex); return StringUtils.isBlank(data) ? null : JsonUtils.fromJson(data, (Class) getRawType()); } @Override public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String data = cs.getString(columnIndex); return StringUtils.isBlank(data) ? null : JsonUtils.fromJson(data, (Class) getRawType()); } } 第二步,定义具体的实现类,继承步骤一中的AbstractObjectTypeHandler,要转什么类型的Java对象,转什么对象。 public class CssTypeHandler extends AbstractObjectTypeHandler { } 第三步,修改原有Element类的数据结构,去除String类型,如图: @Data public class Element { private String id; private Object css; private Datetime createTime; } 第四步,配置类型处理器包扫描路径,在application.properties里面配置: #mybatis typeHandler扫描 mybatis.typeHandlersPackage=com.eqxiu.chart.handler.impl 第五步,修改对应XML文件,将对应属性使用自定义的转换器: 第六步,这里有个小坑,在MySQL8.0后,插入JSON对象还得用uff8mb8编码才行,不然会类型错误。所以在insert或者update时,需要加上CONVERT函数,作编码转换。 INSERT element (id, css, compType, contentIds, chartData, createTime, updateTime, creatorId) VALUES (#{element.id,jdbcType=VARCHAR} , CONVERT(#{element.css,jdbcType=OTHER,typeHandler=com.eqxiu.chart.handler.Impl.CssTypeHandler} using utf8mb4) , #{element.comptype,jdbcType=VARCHAR} , #{element.contentIds,jdbcType=VARCHAR,typeHandler=com.eqxiu.chart.handler.Impl.ListToVarcharTypeHandler} , #{element.chartData,jdbcType=VARCHAR},#{element.createTime,jdbcType=TIMESTAMP},#{element.updateTime,jdbcType=TIMESTAMP} , #{element.creatorId,jdbcType=VARCHAR})

最后,业务代码就变得不再冗杂了,也便于扩展。

@Override public Element getById(String id) { return elementDao.selectByPrimaryKey(id); } @Override public boolean save(Element element) { return elementDao.insert(element) > 0; } 三丶反思,继续扩张认知边界

这篇文章只是从业务实现层面去介绍了如何用TypeHandler的思路,优雅的解决Mybatis+Mysql实现对json数据类型字段的存取。但是对于,MyBatis中TypeHandler如何具体执行的以及设计思路未作探讨,下一篇文章会继续写Mybatis中TypeHandler的原理。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3